/*
    KMC Simulation
    Copyright (c) 2012-2014 Filip Rozbořil

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#pragma once

#include "Exception.h"
#include "Random.h"
#include "Utils.h"

typedef unordered_map<GridAtom*, uint32_t>::value_type AtomIndexPair;

class HopRate
{
	private:
		double mBaseFrequency;
		int_fast32_t mCountChange;
		Direction::type mDir;
		uint32_t mFromToDir;
		vector<GridAtom*> mAtoms;
		
	public:
		HopRate(double rate, Site::type from, Site::type to, Direction::type dir, uint32_t capacity) : mBaseFrequency(rate), mCountChange(0), mDir(dir)
		{ 
			mFromToDir = Site::Count * Direction::Count * from + Direction::Count * to + dir;
			mAtoms.reserve(capacity);
		}
		bool countChanged() {  return mCountChange != 0; }
		void resetCounter() { mCountChange = 0; }
		double getRate() { return mAtoms.size() * mBaseFrequency; }
		uint64_t getHash() { return (uint64_t)mAtoms.size() * (Direction::Count * Site::Count * Site::Count) + mFromToDir; }
		void removeAtom(GridAtom* atom)
		{
			Trace("[HOPRATE] Direction " << Direction::getName(mDir) << ": Removing atom from vector (count: " << mAtoms.size() << ")");
			uint32_t index = atom->getIndex(mDir);
			GridAtom* last = mAtoms.back();
			mAtoms[index] = last;
			last->setIndex(mDir, index);;
			mAtoms.pop_back();
			atom->setSite(mDir, Site::Count);
			mCountChange--;
		}
		void addAtom(GridAtom* atom)
		{
			Trace("[HOPRATE] Direction " << Direction::getName(mDir) << ": Adding atom to vector (count: " << mAtoms.size() << ")");
			atom->setIndex(mDir, mAtoms.size());
			mAtoms.push_back(atom);
			mCountChange++;
		}
		GridAtom* getAtom(Random* r)
		{
		 	return mAtoms[r->getInteger(mAtoms.size())];
		}
		#ifdef GRIDCHECK
		GridAtom* getAtom(uint32_t index)
		{
			return mAtoms[index];
		}
		#endif
};

